#include <cstdio>
#include <tuple>
namespace instantiater {

template <typename... Ts>
struct type_holder {
  template <typename Tp, typename... Tps>
  struct get_first {
    typedef Tp type;
  };

  typedef typename get_first<Ts...>::type first;
};
template <>
struct type_holder<> {
};
template <typename T, typename... Ts>
constexpr type_holder<Ts..., T> push_back(type_holder<Ts...> a) {
  return type_holder<Ts..., T>{};
}
template <typename T, typename... Ts>
constexpr type_holder<Ts...> pop_head(type_holder<T, Ts...> a) {
  return type_holder<Ts...>{};
}
template <typename... T0s, typename... T1s>
constexpr type_holder<T0s..., T1s...> merge(type_holder<T0s...> a, type_holder<T1s...> b) {
  return type_holder<T0s..., T1s...>{};
}
template <typename... Tps, typename... Tcs, typename... Tns>
constexpr auto cartsian_product_types(type_holder<Tps...> prev, type_holder<Tcs...> cur, Tns... next) {
  return merge(
      cartsian_product_types(push_back<typename type_holder<Tcs...>::first>(prev), next...),
      cartsian_product_types(prev, pop_head(cur), next...));
}
template <typename... Tps, typename... Tns>
constexpr auto cartsian_product_types(type_holder<Tps...> prev, type_holder<> cur, Tns... next) {
  return type_holder<>{};
}
template <typename... Ts>
constexpr auto cartsian_product_types(type_holder<Ts...> prev) {
  return type_holder<type_holder<Ts...>>{};
}

template <typename T, T V0, T... Vs>
constexpr T tvfirst() {
  return V0;
}

template <typename T, T... Vs>
struct tvalue_holder {
  static constexpr T first = tvfirst<T, Vs...>();
  typedef T value_type;
};
template <typename T, T V0, T... Vs>
constexpr T tvfirst(tvalue_holder<T, V0, Vs...> hld) {
  return V0;
}
template <typename T>
struct tvalue_holder<T> {
  typedef T value_type;
};
template <typename VT, VT T, VT... Ts>
constexpr tvalue_holder<VT, Ts...> pop_head(tvalue_holder<VT, T, Ts...> a) {
  return tvalue_holder<VT, Ts...>{};
}

template <typename... Ts>
constexpr auto cartsian_product_tvalues(type_holder<Ts...> prev) {
  return type_holder<type_holder<Ts...>>{};
}
template <typename... Tps, typename Tcur, Tcur... Tcs, typename... Tns>
constexpr auto cartsian_product_tvalues(type_holder<Tps...> prev, tvalue_holder<Tcur, Tcs...> cur, Tns... next) {
  return merge(
      cartsian_product_tvalues(push_back<tvalue_holder<Tcur, tvfirst<Tcur, Tcs...>()>>(prev), next...),
      cartsian_product_tvalues(prev, pop_head(cur), next...));
}
template <typename... Tps, typename Tcur, typename... Tns>
constexpr auto cartsian_product_tvalues(type_holder<Tps...> prev, tvalue_holder<Tcur> cur, Tns... next) {
  return type_holder<>{};
}

template <typename... Tns>
constexpr auto cartprod_tv(Tns... next) {
  return cartsian_product_tvalues(type_holder<>{}, next...);
}

template <typename... Tns>
constexpr auto cartprod_t(Tns... next) {
  return cartsian_product_types(type_holder<>{}, next...);
}
template <typename T, typename... Is>
constexpr auto cartsian_product_stmv(T t, type_holder<Is...> is) {
  return type_holder<type_holder<T, Is>...>{};
}
template <typename T0, typename... Ts, typename... Is>
constexpr auto cartsian_product_mtmv(type_holder<T0, Ts...> ts, type_holder<Is...> is) {
  return merge(
      cartsian_product_stmv(T0{}, is),
      cartsian_product_mtmv(pop_head(ts), is));
}

template <typename... Is>
constexpr auto cartsian_product_mtmv(type_holder<> ts, type_holder<Is...> is) {
  return type_holder<>{};
}

template <typename T, typename... Is>
constexpr auto cartsian_product_sttv(T t, type_holder<Is...> is) {
  // return cartsian_product_sttvs(t, Is::first...);
  return type_holder<type_holder<T, Is>...>{};
}
template <typename T0, typename... Ts, typename... Is>
constexpr auto cartsian_product_mttv(type_holder<T0, Ts...> ts, type_holder<Is...> is) {
  return merge(
      cartsian_product_sttv(T0{}, is),
      cartsian_product_mttv(pop_head(ts), is));
}

template <typename... Is>
constexpr auto cartsian_product_mttv(type_holder<> ts, type_holder<Is...> is) {
  return type_holder<>{};
}

template <template <typename... Tps> class proxy, typename... Tts, typename... Tvs>
int call_proxytt(type_holder<type_holder<Tts...>, type_holder<Tvs...>> values) {
  // puts(__PRETTY_FUNCTION__);
  proxy<Tts...>::template run(type_holder<Tvs...>{});
  return 0;
}
template <template <typename... Tps> class proxy, typename... Ts>
void instantiate_alltt(type_holder<Ts...> args) {
  puts(__PRETTY_FUNCTION__);
  int a[sizeof...(Ts)] = {call_proxytt<proxy>(Ts{})...};
}

}; // namespace instantiater

#define __instantiate_func(func, types, values)                      \
  __instantiate_func_u(func, types, values, )

#define __instantiate_func_u(func, types, values, mark)              \
  template <typename... Ts>                                          \
  struct func##_instantiate_proxy##mark {                            \
    template <typename... Vs>                                        \
    static void run(instantiater::type_holder<Vs...> h) {            \
      func<Ts..., instantiater::tvfirst(Vs{})...>();                 \
    }                                                                \
  };                                                                 \
  void func##_instantiater##mark() {                                 \
    auto tts = instantiater::cartprod_t types;                       \
    auto tis = instantiater::cartprod_tv values;                     \
    auto ttis = instantiater::cartsian_product_mttv(tts, tis);       \
    instantiater::instantiate_alltt<func##_instantiate_proxy>(ttis); \
  }

#define THLD(...) \
  instantiater::type_holder<__VA_ARGS__> {}
#define VHLD(t0, ...) \
  instantiater::tvalue_holder<decltype(t0), t0, ##__VA_ARGS__> {}
// enum enum_test {
//   EV0,
//   EV1
// };
// template <typename t0, typename t1, typename t2, int i0, enum_test i1, unsigned int i2>
// void f() {
//   puts(__PRETTY_FUNCTION__);
// }
// template <int a, long b>
// void ttt() {
//   puts(__PRETTY_FUNCTION__);
// }
// template <typename T>
// void print_t(T t) {
//   puts(__PRETTY_FUNCTION__);
// }
// template <typename... Ts>
// struct func_instantiate_proxy {
//   template <typename... Vs>
//   static void run(instantiater::type_holder<Vs...> h) {
//     f<Ts..., instantiater::tvfirst(Vs{})...>();
//   }
// };
// __instantiate_func(f, (THLD(double, int), THLD(int), THLD(int)), (VHLD(1, 2), VHLD(EV0, EV1), VHLD(-1)));
// using namespace instantiater;
// typedef double doublev4 __attribute__((mode(__V4DF__)));
// int main() {
//   f_instantiater();
//   doublev4 t = doublev4(2.0);
// }